package org.ala.tiktools.sharddb.mybatis.interceptor;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.mybatis.expand.ExpandData;
import org.ala.tiktools.mybatis.interceptor.IAnnotationInterceptor;
import org.ala.tiktools.mybatis.interceptor.InterceptResult;
import org.ala.tiktools.sharddb.db.ThreadLocalLookupKey;
import org.ala.tiktools.sharddb.exception.ShardDBRuntimeException;
import org.ala.tiktools.sharddb.mybatis.executor.ShardExecutor;
import org.ala.tiktools.sharddb.mybatis.router.ShardInfo;
import org.ala.tiktools.sharddb.mybatis.router.ShardRouterHandler;
import org.ala.tiktools.sharddb.mybatis.summary.ShardSummaryHandler;
import org.ala.tiktools.tools.CachingKVTools;
import org.ala.tiktools.tools.kv.KV;
import org.ala.tiktools.tools.kv.Maps;
import org.ala.tiktools.tools.thread_pool.ThreadPoolTools;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 分库分表执行拦截器
 * <p>  具有Shard注解的方法，在执行时会走分库分表逻辑
 *
 * @author ala
 * @date 2025-02-19 11:52
 */
public class ShardInterceptor implements IAnnotationInterceptor<Shard> {


    static Logger log = LoggerFactory.getLogger("shard_db", "interceptor");
    /**
     * 路由器
     */
    protected ShardRouterHandler router = new ShardRouterHandler();
    /**
     * 执行器
     */
    protected ShardExecutor executor = new ShardExecutor();
    /**
     * 汇总器
     */
    protected ShardSummaryHandler summary = new ShardSummaryHandler();


    @Override
    public Class<Shard> annotation() {
        return Shard.class;
    }


    /**
     *  1）该方法被递归调用。但每次调用时expandDatas里参数不同
     *  2）当expandDatas里包含完整的分库分表信息时，
     */
    @Override
    public InterceptResult intercept(Shard anno, Object proxy, Method method, Object[] args, Map<String, ExpandData> expandDatas) throws Exception {
        //  如果线程环境中不含分库分表信息，则判定是第一次执行，先分库分表
        //  否则直接执行该分库分表
        ShardInfo shardInfo = ShardInfoThreadLocal.getShardInfo();
        if (shardInfo != null) {
              //    把分表信息放入expandDatas，返回null，执行被代理的方法
              //    分库信息在分配线程池时已经放入线程环境中
            expandDatas.put("shardSuffix", ExpandData.build("shardSuffix", shardInfo.getShardTable(), true));
            return InterceptResult.next();
        }


        //  否则按分库分表信息分别执行
        Map<ShardInfo, Object[]> shardInfos = router.route(proxy, method, args);

        //  是否允许跨库表
        if (!anno.allowSummary() && shardInfos.size() > 1) {
            throw new ShardDBRuntimeException(String.format("不允许跨库表操作，如若开启请配置@Shard(allowSummary=true)。视业务情况而定 method:%s class:%s", method.getName(), method.getDeclaringClass().getSimpleName()));
        }

        //  执行分库分表
        Map<ShardInfo, ShardExecutor. Result> shardResult = executor.doShardInvoke(anno, shardInfos, proxy, method, args, expandDatas);
        //  如果都执行成功进行结果汇总
        return InterceptResult.build(summary.summary(anno, method, args, shardResult), true);
    }
}
